#include "dicomimageview.h"
#include "seriesinstance.h"
#include "imageinstance.h"
#include "Global/globaldefs.h"

#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcmetinf.h"
#include "dcmtk/dcmimgle/dcmimage.h"

#include <QGraphicsPixmapItem>
#include <QGraphicsSimpleTextItem>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QFrame>
#include <QMimeData>
#include <QBitmap>
#include <QFileDialog>
#include <QSettings>

DicomImageView::DicomImageView(ViewType type, SeriesInstance *series, QWidget *parent) :
    scene(new QGraphicsScene(this)),
    pixmapItem(new QGraphicsPixmapItem),
    factor(1.0),
    fixFactor(1.0),
    magFactor(2),
    manualZoom(false),
    manualPan(false),
    hflip(0),
    vflip(0),
    rotateAngle(0),
    vType(type),
    series(0),
    QGraphicsView(parent)
{
    setFocusPolicy(Qt::StrongFocus);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setContextMenuPolicy(Qt::DefaultContextMenu);
    setBackgroundBrush(QBrush(Qt::black));
    setAcceptDrops(true);
    setFrameShape(QFrame::Box);
    setFrameShadow(QFrame::Plain);
    setAlignment(Qt::AlignCenter);
    setResizeAnchor(QGraphicsView::AnchorViewCenter);
    setHighlight(false);

    scene->setSceneRect(-5000, -5000, 10000, 10000);
    setScene(scene);

    subPixmapItem = new QGraphicsPixmapItem(pixmapItem);
    subPixmapItem->setTransformationMode(Qt::SmoothTransformation);
    subPixmapItem->setVisible(false);
    magFactorItem = new QGraphicsSimpleTextItem(subPixmapItem);
    magFactorItem->setBrush(Qt::green);

    double f = QSettings().value(MAGNIFIER_FACTOR).toDouble();
    if (f > 1) magFactor = f;
    QBitmap cur(32, 32), mask(32, 32);
    cur.fill(Qt::color0);
    mask.fill(Qt::color0);
    nullCursor = QCursor(cur, mask);

    pixmapItem->setTransformationMode(Qt::SmoothTransformation);
    pixmapItem->setAcceptHoverEvents(true);

    scene->addItem(pixmapItem);

    setSeriesInstance(series);
}

DicomImageView::~DicomImageView()
{
    QSettings().setValue(MAGNIFIER_FACTOR, magFactor);
    clear();
}

int DicomImageView::getFrameCount() const
{
    if (series) return series->getFrameCount(vType);
    return 0;
}

bool DicomImageView::getImageDimensions(int &x, int &y, int &z) const
{
    if (series) {
        x = series->getFrameCount(VT_YZPlane);
        y = series->getFrameCount(VT_XZPlane);
        z = series->getFrameCount(VT_XYPlane);
        return true;
    }
    return false;
}

const short ** DicomImageView::getSeriesVolume(const short **&volume, ulong &width, ulong &height, ulong &slice) const
{
    return series?series->getSeriesVolume(volume, width, height, slice):0;
}

const ushort **DicomImageView::getRawVolume(const ushort **&volume, ulong &width, ulong &height, ulong &slice) const
{
    return series?series->getRawVolume(volume, width, height, slice):0;
}

void DicomImageView::setSeriesInstance(SeriesInstance *series)
{
    if (this->series && this->series != series) {
        disconnect(this->series, SIGNAL(aboutToDelete()), this, SLOT(clear()));
        this->series->delVolBuffer();
    }
    if (series) {
        connect(series, SIGNAL(aboutToDelete()), this, SLOT(clear()));
    }
    this->series = series;

    reset();

    scene->update(scene->sceneRect());
}

DicomImage *DicomImageView::getHardCopyGrayScaledImage()
{
    if (series && series->getCurrImageInstance(vType)) {
        ImageInstance *image = series->getCurrImageInstance(vType);
        QPointF tl = pixmapItem->mapFromScene(mapToScene(this->rect().topLeft()));
        QPointF br = pixmapItem->mapFromScene(mapToScene(this->rect().bottomRight()));
        if (tl.x() > br.x()) {
            double temp = br.x();
            br.setX(tl.x());
            tl.setX(temp);
        }
        if (tl.y() > br.y()) {
            double temp = br.y();
            br.setY(tl.y());
            tl.setY(temp);
        }

        return image->createClippedImage(QRectF(tl, br).toRect(), rotateAngle%360,
                                         hflip, vflip,
                                         image->getPolarity()==EPP_Reverse);
    }

    return 0;
}

QPixmap DicomImageView::getHardCopyPixmap()
{
    QPixmap pixmap;
    DicomImage *hgImage = getHardCopyGrayScaledImage();
    if (hgImage) ImageInstance::dcm2bmpHelper(*hgImage, pixmap);
    delete hgImage;
    return pixmap;
}

QImage DicomImageView::getRenderedImage()
{
    QImage image(this->rect().size(), QImage::Format_ARGB32);
    QPainter painter(&image);
    render(&painter);
    painter.end();
    return image;
}

void DicomImageView::setHighlight(bool yes)
{
    QPalette p = palette();
    if (yes) p.setColor(QPalette::Text, Qt::magenta);
    else p.setColor(QPalette::Text, Qt::gray);
    setPalette(p);
}

void DicomImageView::setMagnifierPixmap(const QPointF &itemPos)
{
    double srcWidth = (MAGNIFIER_SIZE) / (magFactor*factor);
    QRect source(itemPos.x()-srcWidth/2, itemPos.y()-srcWidth/2, srcWidth, srcWidth);
    QPixmap subImage(source.size());
    QPainter painter(&subImage);
    painter.drawPixmap(QPoint(0, 0), pixmapItem->pixmap(), source);
    painter.end();
    subPixmapItem->setPos(itemPos.x()-MAGNIFIER_SIZE/(2*factor), itemPos.y()-MAGNIFIER_SIZE/(2*factor));
    subPixmapItem->setPixmap(subImage.scaledToWidth(MAGNIFIER_SIZE, Qt::SmoothTransformation));
    subPixmapItem->setScale(1/factor);
    subPixmapItem->setVisible(true);
    magFactorItem->setText(QString("%1X").arg(magFactor));
    magFactorItem->setPos(subPixmapItem->boundingRect().width() - magFactorItem->boundingRect().width(), 0);
}

void DicomImageView::resizePixmapItem()
{
    if (!manualZoom) {
        QRectF pixmapRect = pixmapItem->boundingRect();
        QRectF viewRect = this->rect();
        if (pixmapRect.width() > 0 && pixmapRect.height() > 0) {
            if (pixmapRect.width()*viewRect.height() < pixmapRect.height()*fixFactor*viewRect.width())
                factor = viewRect.height()/(pixmapRect.height()*fixFactor);
            else
                factor = viewRect.width()/pixmapRect.width();
        }
    }

    if (!pixmapItem->pixmap().isNull()) {
        pixmapItem->setTransform(QTransform(factor, 0, 0, factor*fixFactor, 0, 0));
        if (!manualPan) centerOn(pixmapItem);
    }
}

void DicomImageView::mousePressEvent(QMouseEvent *event)
{
    if (series) {
        QPointF sp = mapToScene(event->pos());
        QPointF ip = pixmapItem->mapFromScene(sp);

        prevMousePos = event->pos();
        prevPressedPos = event->pos();

        if (event->button() == Qt::RightButton) {
            setNone();
        } else if (event->button() == Qt::LeftButton) {
            setDragMode(QGraphicsView::NoDrag);
            switch (leftButton.state) {
            case None:
            case ROIWindow:
                if (!(event->modifiers()&Qt::ControlModifier)) {
                    setDragMode(QGraphicsView::RubberBandDrag);
                    setCursor(Qt::ArrowCursor);
                }
                break;
            case Zoom:
                manualZoom = true;
                break;
            case Pan:
                setCursor(Qt::ClosedHandCursor);
                manualPan = true;
                break;
            case Magnifier:
                setCursor(nullCursor);
                setMagnifierPixmap(ip);
                break;
            }
        }
    }

    emit viewClicked(this);
    QGraphicsView::mousePressEvent(event);
}

void DicomImageView::mouseDoubleClickEvent(QMouseEvent *event)
{
    emit viewDoubleclicked(this);
    QGraphicsView::mouseDoubleClickEvent(event);
}

void DicomImageView::mouseMoveEvent(QMouseEvent *event)
{
    if (series) {
        QPointF sp = mapToScene(event->pos());
        QPointF ip = pixmapItem->mapFromScene(sp);

        if (event->buttons()&Qt::RightButton) {
            setDragMode(QGraphicsView::NoDrag);
            setCursor(Qt::ArrowCursor);
            QPoint delta = event->pos() - prevMousePos;
            series->setWindowDelta(-delta.y()*12, delta.x()*12);
            refreshPixmap();
        } else if (event->buttons()&Qt::MiddleButton) {
            setDragMode(QGraphicsView::NoDrag);
            int frames = event->pos().y() - prevMousePos.y();
            if (frames > 0) series->nextFrame(vType);
            else  series->prevFrame(vType);
            refreshPixmap();
        } else if (event->modifiers()&Qt::ControlModifier) {
            setDragMode(QGraphicsView::NoDrag);
            if ((event->buttons() & Qt::LeftButton)) {
                setCursor(Qt::ClosedHandCursor);
                manualPan = true;
                scene->clearSelection();
                if (scene->mouseGrabberItem()) scene->mouseGrabberItem()->ungrabMouse();
                QPoint deltaPos = event->pos() - prevMousePos;
                pixmapItem->setPos(pixmapItem->pos()+deltaPos);
            } else setCursor(Qt::OpenHandCursor);
        } else {
            switch (leftButton.state) {
            case None:
            case ROIWindow:
                setCursor(Qt::ArrowCursor);
                break;
            case Zoom:
                setCursor(Qt::SizeAllCursor);
                if (event->buttons() & Qt::LeftButton) {
                    scene->clearSelection();
                    if (scene->mouseGrabberItem()) scene->mouseGrabberItem()->ungrabMouse();
                    int delta = prevMousePos.y() - event->pos().y();
                    if (delta > 0) factor *= (1+delta*0.02);
                    else factor /= (1-delta*0.02);
                    //pixmapItem->setTransformOriginPoint(itemPoint);
                    resizePixmapItem();
                }
                break;
            case Pan:
                if ((event->buttons() & Qt::LeftButton)) {
                    setCursor(Qt::ClosedHandCursor);
                    scene->clearSelection();
                    if (scene->mouseGrabberItem()) scene->mouseGrabberItem()->ungrabMouse();
                    QPoint deltaPos = event->pos() - prevMousePos;
                    pixmapItem->setPos(pixmapItem->pos()+deltaPos);
                } else setCursor(Qt::OpenHandCursor);
                break;
            case Magnifier:
                if (event->buttons() & Qt::LeftButton) {
                    setCursor(nullCursor);
                    scene->clearSelection();
                    if (scene->mouseGrabberItem()) scene->mouseGrabberItem()->ungrabMouse();
                    setMagnifierPixmap(ip);
                } else {
                    setCursor(Qt::SizeAllCursor);
                }
                break;
            case AdjustWL:
                if (event->buttons() & Qt::LeftButton) {
                    setDragMode(QGraphicsView::NoDrag);
                    setCursor(Qt::ArrowCursor);
                    QPoint delta = event->pos() - prevMousePos;
                    series->setWindowDelta(-delta.y()*12, delta.x()*12);
                    refreshPixmap();
                }
                break;
            case Slicing:
                if (event->buttons() & Qt::LeftButton) {
                    setDragMode(QGraphicsView::NoDrag);
                    int frames = event->pos().y() - prevMousePos.y();
                    if (frames > 0) series->nextFrame(vType);
                    else  series->prevFrame(vType);
                    refreshPixmap();
                }
                break;
            }
        }
        prevMousePos = event->pos();
    }

    QGraphicsView::mouseMoveEvent(event);
}

void DicomImageView::mouseReleaseEvent(QMouseEvent *event)
{
    if (series) {
        if (event->button() == Qt::LeftButton) {
            switch (leftButton.state) {
            case Drawing:
                /*
                if ((leftButton.type == DrawCurve || leftButton.type == DrawTextMark) && currentPathItem) {
                    itemList.append(currentPathItem);
                    currentPathItem->ungrabMouse();
                    currentPathItem->nextStage();
                    currentPathItem = 0;
                }
                */
                break;
            case Pan:
                setCursor(Qt::OpenHandCursor);
                break;
            case Magnifier:
                setCursor(Qt::SizeAllCursor);
                subPixmapItem->setVisible(false);
                break;
            case None:
                if (scene->selectedItems().size() == 0) {
                    if (rubberBandRect().isValid()) {
                        series->setRoiWindow(pixmapItem->mapFromScene(mapToScene(rubberBandRect())).boundingRect());
                        refreshPixmap();
                    }
                }
                break;
            }
        }
    }

    QGraphicsView::mouseReleaseEvent(event);
}

void DicomImageView::wheelEvent(QWheelEvent *e)
{
    QPoint delta = e->angleDelta();
    if (series == 0) return;

    if ((e->buttons() & Qt::LeftButton) && (leftButton.state == Magnifier)) {
        if (delta.y() > 0) magFactor += 1;
        else magFactor -= 1;

        QPointF sp = mapToScene(e->pos());
        setMagnifierPixmap(pixmapItem->mapFromScene(sp));
    } else if (e->modifiers() & Qt::ControlModifier) {
        manualZoom = true;
        if (delta.y() > 0) factor *= (1+delta.y()*0.02);
        else factor /= (1-delta.y()*0.02);
        resizePixmapItem();
    } else {
        if (delta.y() > 0) prevFrame();
        else if (delta.y() < 0) nextFrame();
    }
}

void DicomImageView::nextFrame()
{
    series->nextFrame(vType);
    refreshPixmap();
}

void DicomImageView::prevFrame()
{
    series->prevFrame(vType);
    refreshPixmap();
}

void DicomImageView::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Delete) {
    } else if (event->key() == Qt::Key_Up) {
        prevFrame();
    } else if (event->key() == Qt::Key_Down) {
        nextFrame();
    } else if (event->key() == Qt::Key_Left) {
    } else if (event->key() == Qt::Key_Right) {
    } else if (event->key() == Qt::Key_Escape) {
        setNone();
    } else if (event->key() == Qt::Key_Control) {
        if (leftButton.state == None) setCursor(Qt::OpenHandCursor);
    } else {
        QGraphicsView::keyPressEvent(event);
    }
}

void DicomImageView::keyReleaseEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Control &&
            leftButton.state == None)
        setCursor(Qt::ArrowCursor);
    else QGraphicsView::keyReleaseEvent(event);
}

void DicomImageView::resizeEvent(QResizeEvent *event)
{
    QGraphicsView::resizeEvent(event);

    if (!manualZoom) setFillViewport();
    resizePixmapItem();
}

void DicomImageView::dragEnterEvent(QDragEnterEvent *e)
{
    if (e->mimeData()->hasFormat("text/plain"))
        e->acceptProposedAction();
}

void DicomImageView::dragMoveEvent(QDragMoveEvent *e)
{
    if (e->mimeData()->hasFormat("text/plain"))
        e->acceptProposedAction();
}

void DicomImageView::dropEvent(QDropEvent *e)
{
    e->acceptProposedAction();
    SeriesInstance *s = qobject_cast<SeriesInstance*>((QObject*)(e->mimeData()->text().toULongLong()));
    if (s) {
        setSeriesInstance(s);
        emit viewClicked(this);
    }
}

void DicomImageView::adjustWL()
{
    if (scene->mouseGrabberItem()==0 && series) {
        scene->clearSelection();
        leftButton.state = AdjustWL;
        setCursor(Qt::ArrowCursor);
    }
}

void DicomImageView::setDefaultWL()
{
    if (series) {
        series->setDefaultWindow();
        refreshPixmap();
    }
}

void DicomImageView::setFullDynamic()
{
    if (series) {
        series->setFullDynamic();
        refreshPixmap();
    }
}

void DicomImageView::inverseImage()
{
    if (series) {
        series->setPolarity(series->getPolarity()==EPP_Normal?EPP_Reverse:EPP_Normal);
        refreshPixmap();
    }
}

void DicomImageView::setRoiWindow()
{
    if (scene->mouseGrabberItem()==0 && series) {
        scene->clearSelection();
        leftButton.state = ROIWindow;
        setCursor(Qt::ArrowCursor);
    }
}

void DicomImageView::setNone()
{
    leftButton.state = None;
    setCursor(Qt::ArrowCursor);
}

void DicomImageView::refreshPixmap()
{
    QPixmap pixmap;
    if (series) {
        series->getPixmap(pixmap, vType);
        pixmapItem->setPixmap(pixmap);
        pixmapItem->setTransformOriginPoint(pixmapItem->boundingRect().center());
        double center, width;
        series->getWindow(center, width);
    } else {
        pixmapItem->setPixmap(pixmap);
    }
}

void DicomImageView::rotateCCW()
{
    if (series) {
        rotateAngle += 270;
        pixmapItem->setRotation(rotateAngle);
    }
}

void DicomImageView::rotateCW()
{
    if (series) {
        rotateAngle += 90;
        pixmapItem->setRotation(rotateAngle);
    }
}

void DicomImageView::clearRotate()
{
    pixmapItem->setRotation(0);
    rotateAngle = 0;
}

void DicomImageView::zoomImage()
{
    if (scene->mouseGrabberItem()==0 && series) {
        scene->clearSelection();
        leftButton.state = Zoom;
        setCursor(Qt::SizeAllCursor);
    }
}

void DicomImageView::setFillViewport()
{
    bool b = manualZoom;
    manualZoom = false;
    resizePixmapItem();
    centerOn(pixmapItem);
    manualZoom = b;
}

void DicomImageView::setZoom100()
{
    bool b = manualZoom;
    manualZoom = true;
    factor = 1;
    resizePixmapItem();
    manualZoom = b;
}

void DicomImageView::setZoom200()
{
    bool b = manualZoom;
    manualZoom = true;
    factor = 2;
    resizePixmapItem();
    manualZoom = b;
}

void DicomImageView::setZoom400()
{
    bool b = manualZoom;
    manualZoom = true;
    factor = 4;
    resizePixmapItem();
    manualZoom = b;
}

void DicomImageView::setZoomin()
{
    bool b = manualZoom;
    manualZoom = true;
    factor *= 1.2;
    resizePixmapItem();
    manualZoom = b;
}

void DicomImageView::setZoomout()
{
    bool b = manualZoom;
    manualZoom = true;
    factor *= 0.8;
    resizePixmapItem();
    manualZoom = b;
}

void DicomImageView::magnifier()
{
    if (scene->mouseGrabberItem() == 0 && series) {
        scene->clearSelection();
        leftButton.state = Magnifier;
        setCursor(Qt::SizeAllCursor);
    }
}

void DicomImageView::hFlip()
{
    if (series) {
        QRectF pRect = pixmapItem->boundingRect();
        pixmapItem->setTransform(QTransform(-1, 0, 0, 1, pRect.width(), 0), true);
        hflip = !hflip;
    }
}

void DicomImageView::vFlip()
{
    if (series) {
        QRectF pRect = pixmapItem->boundingRect();
        pixmapItem->setTransform(QTransform(1, 0, 0, -1, 0, pRect.height()), true);
        vflip = !vflip;
    }
}

void DicomImageView::clearFlip()
{
    if (hflip) hFlip();
    if (vflip) vFlip();
}

void DicomImageView::panImage()
{
    if (scene->mouseGrabberItem()==0 && series) {
        scene->clearSelection();
        leftButton.state = Pan;
        setCursor(Qt::OpenHandCursor);
        manualPan = true;
    }
}

void DicomImageView::resetPan()
{
    pixmapItem->setPos(0, 0);
    manualPan = false;
    centerOn(pixmapItem);
}

void DicomImageView::slicing()
{
    if (scene->mouseGrabberItem()==0 && series) {
        scene->clearSelection();
        leftButton.state = Slicing;
    }
}

void DicomImageView::saveFile()
{
    if (series && series->getCurrImageInstance(vType) && series->getCurrImageInstance(vType)->getFileFormat()) {
        DcmFileFormat *ff = series->getCurrImageInstance(vType)->getFileFormat();
        QSettings s;
        QString file = s.value(SAVE_FILEPATH, ".").toString();
        file = QFileDialog::getSaveFileName(this, tr("Save Image"), file,
                                            tr("DICOM (*.dcm);;RAW (*.raw)"));
        if (!file.isEmpty()) {
            s.setValue(SAVE_FILEPATH, file);
            if (file.right(4) == ".dcm") {
                ff->saveFile(file.toLocal8Bit().data(), ff->getMetaInfo()->getOriginalXfer());
            } else if (file.right(4) == ".raw") {
                const ushort *pixelData;
                ulong count;
                if (ff->getDataset() && (ff->getDataset()->findAndGetUint16Array(DCM_PixelData, pixelData, &count) == EC_Normal) && pixelData) {
                    QFile rawFile(file);
                    if (rawFile.open(QIODevice::WriteOnly)) {
                        rawFile.write((char*)pixelData, count*sizeof(ushort));
                        rawFile.close();
                    }
                }
            }
        }
    }
}

void DicomImageView::reset()
{
    pixmapItem->setPos(0, 0);
    pixmapItem->setRotation(0);
    rotateAngle = 0;
    pixmapItem->resetTransform();
    hflip = false;
    vflip = false;
    manualZoom = false;
    manualPan = false;
    leftButton.state = None;
    setCursor(Qt::ArrowCursor);
    if (series) {
        if (series->getPolarity() != EPP_Normal)
            series->setPolarity(EPP_Normal);
        series->setDefaultWindow();
    }
    refreshPixmap();
    setFillViewport();
}
